<!--
 * @Author: 木子李
 * @Date: 2023-02-08 14:48:58
 * @LastEditors: 木子李
 * @LastEditTime: 2023-02-13 18:21:53
 * @FilePath: \arithmetic\frontend\src\views\shuxue\0_youeryuan\Index.vue
 * @文件介绍: 幼儿园
-->
<template>
  <div id="app-other">
    <!-- <div class="one-block-1">
      幼儿园
    </div> -->
    <div class="one-block-2">
      <a-row>
        <a-col :span="3"
               class="one-block-3">
          数值范围
        </a-col>
        <a-col :span="9">
          <a-input-group compact>
            <a-input-number v-model="formState.min"
                            :max="formState.max"
                            :step="10"
                            style="width: 100px; text-align: center" />
            <a-input style="width: 30px; border-left: 0; pointer-events: none; background-color: #fff"
                     placeholder="~"
                     disabled />
            <a-input-number v-model="formState.max"
                            :min="formState.min"
                            :step="10"
                            style="width: 100px; text-align: center; border-left: 0" />
          </a-input-group>
        </a-col>
        <a-col :span="3"
               class="one-block-3">
          运算规则
        </a-col>
        <a-col :span="9"
               class="one-block-3">
          <a-checkbox-group v-model="formState.yunsuan"
                            :options="yunsuanOptions" />
        </a-col>
      </a-row>

      <a-row class="one-block-2">
        <a-col :span="3">
          计算项
        </a-col>
        <a-col :span="9">
          <a-radio-group v-model="formState.count"
                         :options="countOptions" />
        </a-col>
        <a-col :span="3">
          特殊规则
        </a-col>
        <a-col :span="9">
          <a-checkbox-group v-model="formState.reguler"
                            :options="regulerOptions" />
        </a-col>
      </a-row>

      <a-row class="one-block-2">
        <a-col :span="3"
               class="one-block-3">
          题目数量
        </a-col>
        <a-col :span="9">
          <a-input-number v-model="formState.num"
                          :min="0"
                          :step="10" />
        </a-col>
      </a-row>

      <a-tabs>
        <a-tab-pane key="1"
                    tab="数据">

          <a-radio-group size="small">
            <a-radio-button :disabled="formState.yunsuan.length==0"
                            @click="chuti">出题</a-radio-button>
            <a-radio-button :disabled="dataSource.length==0"
                            @click="exportDataEvent">导出</a-radio-button>
          </a-radio-group>
          <vxe-table border
                     ref="xTable"
                     size="mini"
                     class="one-block-2"
                     :row-config="{isHover: true}"
                     :import-config="{}"
                     :export-config="{}"
                     :data="dataSource">
            <vxe-column field="key"
                        title="序号"
                        width="60"></vxe-column>
            <vxe-column field="topic"
                        title="题目"></vxe-column>
            <vxe-column field="answer"
                        title="答案"></vxe-column>
          </vxe-table>
        </a-tab-pane>
        <a-tab-pane key="2"
                    tab="打印预览"
                    force-render>

          <a-form layout="inline"
                  :model="formState">
            <a-form-item label="打印列数">
              <a-input-number v-model="formState.printCount"
                              :min="1"
                              :max="5" />
            </a-form-item>
            <a-form-item label="每行高度">
              <a-input-number v-model="formState.printRowHeight"
                              :min="30"
                              :max="300"
                              :step="10" />
            </a-form-item>
            <a-form-item label="字体大小">
              <a-input-number v-model="formState.printFontSize"
                              :min="14"
                              :max="300" />
            </a-form-item>
            <a-form-item>
              <a-button type="primary"
                        @click="printEvent">打印</a-button>
            </a-form-item>
          </a-form>

          <div class="one-block-2"
               id="myPrint">
            <div v-for="(r,index) in dataPrint"
                 :key="index"
                 :style="rowStyle">
              <div :span="24/formState.printCount"
                   v-for="c in r"
                   :key="c.key"
                   :style="colStyle">
                <span style="margin-right:10px"> 题{{c.key}} : </span>
                <span> {{ c.topic }}</span>
              </div>
            </div>
          </div>
        </a-tab-pane>
      </a-tabs>
    </div>
  </div>
</template>
<script>
import XEUtils from 'xe-utils'
import VXETable from 'vxe-table'

export default {
  data () {
    return {
      rowStyle: {
        height: "30px",
        width: "100%",
        'font-size': "14px"
      },
      colStyle: {
        display: "inline-block",
        width: "50%",
      },
      yunsuanOptions: [
        { label: '加', value: '+' },
        { label: '减', value: '-' },
        { label: '乘', value: '*' },
        { label: '除', value: '/' },
      ],
      countOptions: [
        { label: '二个数', value: '2' },
        { label: '三个数', value: '3' },
        { label: '四个数', value: '4' },
      ],
      regulerOptions: [
        { label: '小数', value: '小数' },
        { label: '反算', value: '反算' },
        { label: '负数', value: '负数' },
      ],
      formState: {
        min: 0,
        max: 100,
        num: 10,
        yunsuan: ['+'],
        count: "2",
        reguler: [],
        printCount: 2,
        printRowHeight: 30,
        printFontSize: 14
      },
      dataSource: [],
      dataPrint: [],
      columns: [
        {
          title: '题目',
          dataIndex: 'topic',
          key: 'topic',
        }, {
          title: '答案',
          dataIndex: 'answer',
          key: 'answer',
        }
      ]
    }
  },
  watch: {
    "formState.printCount" (newValue, oldValue) {
      this.colStyle.width = XEUtils.round(100 / newValue, 2) + "%"
      this.printData()
    },
    "formState.printRowHeight" (newValue, oldValue) {
      this.rowStyle.height = newValue + "px"
      this.printData()
    },
    "formState.printFontSize" (newValue, oldValue) {
      this.rowStyle['font-size'] = newValue + "px"
      this.printData()
    }
  },
  computed: {
  },
  methods: {
    // 打印数据
    printData () {
      this.dataPrint = []
      for (let i = 0; i < this.dataSource.length; i = i + this.formState.printCount)
      {
        this.dataPrint.push(this.dataSource.slice(i, i + this.formState.printCount))
      }
    },
    // 打印
    printEvent () {
      const divEl = document.getElementById('myPrint')
      VXETable.print({
        sheetName: '数学题',
        content: divEl.innerHTML
      })
    },
    // 生成 [ min, max ] 范围内的随机整数
    randomMM (min, max, round = true) {
      if (round)
      {
        return XEUtils.round(Math.random() * (max - min + 1), 2) + min
      } else
      {
        return Math.floor(Math.random() * (max - min + 1)) + min
      }
    },
    // 生成 [ 0, n ) 范围内的随机整数
    randomN (n) {
      return Math.floor(Math.random() * n)
    },
    // 中缀表达式转后缀表达式
    expT (exp) {
      // 定义一个栈
      let stack = ["#"]
      // 后缀表达式
      let tokens = []
      /*
        #   运算优先级为 0
        (   运算优先级为 0
        )   运算优先级为 0
        +   运算优先级为 1
        -   运算优先级为 1
        *   运算优先级为 2
        /   运算优先级为 2
      */
      let priority = { "#": 0, "(": 0, ")": 0, "+": 1, "-": 1, "*": 2, "/": 2 }
      XEUtils.arrayEach(exp, item => {
        if (["+", "-", "*", "/", "(", ")"].includes(item))
        {
          if (item == "(" || priority[item] > priority[stack.slice(-1)])
          {
            /*
              当前运算符与栈顶元素比较，
              如果当前运算符优先级高于栈顶运算符优先级
              则将当前运算符入栈
            */
            stack.push(item)
          } else if (item == ")")
          {
            // 若当前字符为“)”，则从栈顶起，依次将栈中运算符出栈成为后缀表达式的一部分，直到碰到“(”。将栈中“(”出栈，
            // ( ) 都不需要成为后缀表达式的一部分
            let stack_re = XEUtils.clone(stack).reverse()
            for (let i in stack_re)
            {
              if (stack_re[i] == "(")
              {
                stack.pop()
                break
              } else
              {
                tokens.push(stack.pop())
              }
            }
          }
          else
          {
            /*
              若当前运算符优先级小于等于栈顶运算符优先级，
              循环将栈顶运算符弹出，送入后缀表达式，
              直至栈顶运算符（包括左括号）低于（不包括等于）当前运算符优先级时停止弹出运算符，最后将该运算符送入S1栈。
            */
            let stack_re = XEUtils.clone(stack).reverse()
            for (let i in stack_re)
            {
              if (priority[item] > priority[stack_re[i]])
              {
                stack.push(item)
                break
              } else
              {
                tokens.push(stack.pop())
              }
            }
          }
        } else
        {
          tokens.push(item)
        }
      })
      // 将栈中的所有元素逆序放入表达式
      let stack_re = XEUtils.clone(stack).slice(1).reverse()
      tokens = XEUtils.flatten([tokens, stack_re])
      return tokens
    },
    // 计算后缀表达式
    evalRPN (tokens, minus = false) {
      // 定义一个栈
      let stack = []
      // 是否是负数
      let isFushu = this.formState.reguler.includes("负数")
      // 是否是小数
      let isXisoshu = this.formState.reguler.includes("小数")
      // 循环前缀表达式
      for (let i in tokens)
      {
        let item = tokens[i]
        if (["+", "-", "*", "/"].includes(item))
        {
          // 如果当前值是运算符，则取出栈顶的两个数进行运算，并将结果从新入栈
          let a = stack.pop()
          let b = stack.pop()
          switch (item)
          {
            case "+":
              if (a + b > this.formState.max)
              {
                // 如果结果大于最大值，则退出
                return [0, true]
              } else
              {
                stack.push(a + b)
              }
              break;
            case "-":
              if (!isFushu && (b - a) < 0)
              {
                // 如果不需要负数，但结果是负数，则退出
                return [0, true]
              } else
              {
                stack.push(b - a)
              }
              break;
            case "*":
              if (XEUtils.round(b * a, 2) > this.formState.max)
              {
                // 如果结果大于最大值，则退出
                return [0, true]
              } else
              {
                stack.push(XEUtils.round(b * a, 2))
              }
              break;
            case "/":
              stack.push(XEUtils.round(b / a, 2))
              break;
          }
        } else
        {
          stack.push(Number(item))
        }
      }
      if (!isXisoshu && XEUtils.isFloat(stack[0]))
      {
        // 如果不是小数，但结果是小数，则退出
        return [0, true]
      }
      if (stack[0] > this.formState.max)
      {
        // 如果结果大于最大值，则退出
        return [0, true]
      } else
      {
        return [stack[0], false]
      }
    },
    // 根据条件生成题目
    randomExp () {
      // 参数列表
      let item_list = []
      for (let i = 1; i <= this.formState.count * 1; i++)
      {
        // 生成随机数字
        const num = this.randomMM(this.formState.min, this.formState.max, this.formState.reguler.includes("小数"))
        const ys_num = this.randomN(this.formState.yunsuan.length)
        item_list.push(num)
        // 如果是最后一个数值，则不加运算符号
        if (i < this.formState.count)
        {
          item_list.push(this.formState.yunsuan[ys_num])
        }
      }
      return item_list
    },

    // 筛选出满足条件的题目
    okExp () {
      let item_list // 参数列表
      let rpn_list // 后缀表达式
      let eval_sum // 计算结果
      let logcal // 判断条件
      let topic // 题目
      let answer  // 答案
      // 循环出题
      do
      {

        // 出题
        item_list = this.randomExp()
        // 转出后缀表达式
        rpn_list = this.expT(item_list)
        // 计算后缀表达式
        let eval_rpn = this.evalRPN(rpn_list)
        logcal = eval_rpn[1]
        eval_sum = XEUtils.round(eval_rpn[0], 2)
      }
      while (logcal)
      // debugger
      item_list = XEUtils.flatten([item_list, ["=", eval_sum]])
      // 如果需要反算
      if (this.formState.reguler.includes("反算"))
      {
        // 随机偶数
        let num = this.randomN(item_list.length)
        num = num % 2 == 0 ? num : num - 1

        // 题目
        topic = XEUtils.clone(item_list)
        topic[num] = "____"
        topic = topic.join(" ")
        // 答案
        answer = item_list[num]
      } else
      {
        // 题目
        topic = item_list.slice(0, -1)
        topic.push("____")
        topic = topic.join(" ")
        // 答案
        answer = eval_sum
      }

      topic = topic.replaceAll("/", "÷")
      topic = topic.replaceAll("*", "x")
      return { topic, answer }
    },

    //  出题
    chuti () {
      this.dataSource = []
      let exp
      for (let i = 1; i <= this.formState.num; i++)
      {
        exp = this.okExp()
        exp["key"] = i
        this.dataSource.push(exp)
      }
      // debugger
      // 调用打印数据函数
      this.printData()
    },
    // 导出
    exportDataEvent () {
      this.$refs.xTable.openExport({
        filename: "计算题",
        types: ['csv', "txt"]
      })
    }
  },
}
</script>
<style lang="less" scoped>
#app-other {
  padding: 0px 10px;
  text-align: left;
  width: 100%;
  .one-block-1 {
    font-size: 16px;
    padding-top: 10px;
  }
  .one-block-2 {
    padding-top: 10px;
    .one-block-3 {
      padding-top: 5px;
    }
  }
  .myPrintClass {
    font-size: x-large;
  }
}
</style>
